package com.fp.m7.dao;

import java.util.List;

import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;

import com.fp.m7.model.Account;
import com.fp.m7.model.Follow;
import com.fp.m7.repository.AccountRepository;
import com.google.common.collect.Lists;

public class AccountDaoImpl extends BaseDaoImpl<Account, AccountRepository>
		implements AccountDao {

	@Override
	public Account findByUsername(String username) {
		return repository.findByUsername(username);
	}

	@Override
	public Account findByNickname(String nickname) {
		return repository.findByNickname(nickname);
	}

	@Override
	public List<Account> findRandom(int count) {
		// TODO
		return Lists.newArrayList();
	}

	@Override
	public void follow(Account follower, Account followed) {
		com.fp.m7.model.Account followerCast = repository.findOne(follower
				.getId());
		if (!isFollowing(followerCast, followed)) {
			com.fp.m7.model.Account followedCast = (com.fp.m7.model.Account) followed;

			Follow follow = new Follow();
			follow.setFollowedId(followedCast.getId());
			follow.setAuditCD(clock.getDateAsLong());

			followerCast.getFollowing().add(follow);

			repository.save(followerCast);
		}
	}

	@Override
	public void unfollow(Account follower, Account followed) {
		com.fp.m7.model.Account followerCast = repository.findOne(follower
				.getId());
		Follow activeFollow = getActiveFollow(followerCast, followed);

		if (activeFollow != null) {
			activeFollow.setAuditDD(clock.getDateAsLong());
			repository.save((com.fp.m7.model.Account) followerCast);
		}
	}

	@Override
	public boolean isFollowing(Account user, Account other) {
		Account userFromDb = repository.findOne(user.getId());
		return getActiveFollow(userFromDb, other) != null;
	}

	@Override
	public boolean isFollowedBy(Account user, Account other) {
		Account otherFromDb = repository.findOne(other.getId());
		return isFollowing(otherFromDb, user);
	}

	@Override
	public List<Account> getFollowersForUser(Account user) {
		Account fromDb = repository.findOne(user.getId());
		return template.find(
				Query.query(Criteria.where("following.followedId")
						.is(fromDb.getId()).and("following.auditDD")
						.exists(false)), Account.class);
	}

	@Override
	public List<Account> getFollowedByUser(Account user) {
		Account fromDb = repository.findOne(user.getId());
		List<ObjectId> ids = Lists.newArrayList();
		for (Follow follow : fromDb.getFollowing()) {
			if (follow.getAuditDD() == null) {
				ids.add(follow.getFollowedId());
			}
		}
		if (!ids.isEmpty()) {
			return template.find(Query.query(Criteria.where("id").in(ids)),
					Account.class);
		} else {
			return Lists.newArrayList();
		}
	}

	@Override
	public int countActiveFollowers(Account followed) {
		long count = template.count(
				Query.query(Criteria.where("following.followedId")
						.is(followed.getId()).and("following.auditDD")
						.exists(false)), Account.class);
		return (int) count;
	}

	@Override
	public int countActivelyFollowed(Account follower) {
		Account fromDb = repository.findOne(follower.getId());
		int count = 0;
		for (Follow follow : fromDb.getFollowing()) {
			if (follow.getAuditDD() == null) {
				++count;
			}
		}
		return count;
	}

	private Follow getActiveFollow(Account user, Account other) {
		com.fp.m7.model.Account follower = (com.fp.m7.model.Account) user;
		com.fp.m7.model.Account followed = (com.fp.m7.model.Account) other;

		ObjectId followedId = followed.getId();
		for (Follow follow : follower.getFollowing()) {
			if (followedId.equals(follow.getFollowedId())) {
				if (follow.getAuditDD() == null) {
					return follow;
				}
			}
		}
		return null;
	}

}
